feat(init): add init command for guided Sentry project setup#283
feat(init): add init command for guided Sentry project setup#283
Conversation
Adds `sentry init` wizard that walks users through project setup via the Mastra API, handling DSN configuration, SDK installation prompts, and local file operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sends tags and metadata (CLI version, OS, arch, node version) with startAsync and resumeAsync calls so workflow runs are visible and filterable in Mastra Studio. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import randomBytes and generate a hex trace ID so all suspend/resume calls within a single wizard run share one trace. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a synthetic parentSpanId to tracingOptions so all workflow run spans become siblings under the same parent instead of nesting by timestamp containment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The parentSpanId was creating artificial nesting - let the workflow engine handle span hierarchy naturally. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Display the branded SENTRY ASCII banner before the intro line for visual consistency with `sentry --help`. Make the "errors" feature always enabled in the feature multi-select so users cannot deselect error monitoring. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pt, and source maps hint Route success-with-exitCode results to formatError so the --force hint is shown when Sentry is already installed. Fold the "Error Monitoring is always included" note into the multiselect prompt. Use a more approachable Source Maps hint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show a non-blocking info note about AI usage with a docs link before the first network call, and a review reminder before the success outro. Extract SENTRY_DOCS_URL constant to share between wizard-runner and clack-utils cancel message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add @anthropic-ai/sdk and openai as devDependencies for the LLM-as-judge eval framework. Add opencode-lore dependency. Exclude test/init-eval/templates from biome linting since they are fixture apps, not source code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add LLM-as-judge eval tests for the init wizard across all five platforms (Express, Next.js, Flask, React+Vite, SvelteKit). Each test runs the wizard end-to-end and asserts on SDK installation, Sentry.init presence, build success, and documentation accuracy via an LLM judge. Includes template apps, helper utilities (assertions, doc-fetcher, judge, platform configs), and feature-docs.json mapping. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a separate workflow for running init-eval tests on demand. Supports running a single platform or all platforms via matrix. Uses the init-eval GitHub environment for MASTRA_API_URL and OPENAI_API_KEY secrets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store python-fastapi doc URLs as base paths (with trailing slash) like other platforms, and convert to .md at fetch time. This mirrors the pattern in cli-init-api and lets us return clean markdown directly instead of stripping HTML tags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Sentry doc URLs for python-flask (getting-started, errors, tracing, logs, profiling) and add the shared python/profiling page to both flask and fastapi profiling entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Sentry doc URLs for all nextjs features: getting-started, errors, logs, tracing, session replay, metrics, and profiling (browser + node). Sourcemaps left empty for now. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Sentry doc URLs for sveltekit features and add missing logs, metrics, and profiling features to the platform entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Sentry doc URLs for react-vite features and add missing logs, metrics, and profiling features to the platform entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Flask eval was using bare `pip install` which fails when pip isn't on PATH. Use the same venv pattern as fastapi. Also remove accidental opencode-lore runtime dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨Trace
Other
Bug Fixes 🐛Api
Formatters
Setup
Upgrade
Other
Documentation 📚
Internal Changes 🔧Api
Other
🤖 This preview updates automatically when you update the PR. |
Codecov Results 📊✅ 101 passed | Total: 101 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 95.87%. Project has 3494 uncovered lines. Files with missing lines (8)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 81.52% 82.80% +1.28%
==========================================
Files 127 135 +8
Lines 19227 20312 +1085
Branches 0 0 —
==========================================
+ Hits 15673 16818 +1145
- Misses 3554 3494 -60
- Partials 0 0 —Generated by Codecov Action |
Restrict GITHUB_TOKEN to contents:read as flagged by CodeQL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update SvelteKit template with working deps (adapter-node, latest svelte/vite) and add required src files (app.d.ts, app.html). Use python3 instead of python for venv creation in Flask/FastAPI platforms. Add --concurrency 6 to init-eval test runner. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add push/pull_request triggers so the eval runs automatically alongside other CI checks. Keep workflow_dispatch for manual single-platform runs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rst-token limitation, unexport FEATURE_INFO - Add `#` to shell metacharacter blocklist to prevent command truncation (e.g. `npm install evil-pkg # @sentry/node`) - Document Layer 3's first-token-only limitation with explanatory comment - Remove unnecessary `export` from `FEATURE_INFO` in clack-utils.ts - Add test for shell comment character blocking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…runtime assertions - Add WizardOutput, SelectPayload, MultiSelectPayload, ConfirmPayload, SuspendPayload types; refactor InteractivePayload as discriminated union - Remove ~20 unsafe casts across formatters, interactive, and wizard-runner - Restructure runCommands to validate all commands before executing any - Add assertWorkflowResult/assertSuspendPayload runtime validation for server responses - Add tests for malformed responses, batch validation, and dry-run paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- safePath() now resolves symlinks and rejects paths that escape the project directory via symlink (e.g. link → /etc) - Add withTimeout() helper to race Mastra API calls against a deadline - Bump API_TIMEOUT_MS to 120s to match DEFAULT_COMMAND_TIMEOUT_MS - Delete duplicate isolated test file; consolidate tests in test/lib/ - Add JSDoc to safePath() and withTimeout() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…or patchsets Restore MASTRA_API_URL to production Cloudflare Worker with env var override (was hardcoded to localhost:8787). Add upfront safePath() validation in applyPatchset() so no files are written if any patch targets an unsafe path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…llback (#333) ## Summary Adds the `create-sentry-project` local operation so the remote workflow can ask the CLI to create a Sentry project. Resolves the org via local config / env vars first, falling back to listing orgs from the API (auto-selects if only one, prompts interactively if multiple). ## Changes - New `createSentryProject` handler in `local-ops.ts` with extracted `resolveOrgSlug` helper that handles all org resolution paths (config, single-org auto-select, multi-org interactive prompt, `--yes` guard) - `CreateSentryProjectPayload` type added to `types.ts` - Test suite covering success, single-org fallback, no-orgs, multi-org `--yes`, interactive select, user cancel, API error, and missing DSN paths - Downstream mock setup extracted into `mockDownstreamSuccess` helper to reduce test duplication ## Test Plan ```bash bun test test/lib/init/local-ops.create-sentry-project.test.ts # 8 pass bun run lint # clean ``` --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
| entry.name !== "node_modules" | ||
| ) { | ||
| walk(path.join(dir, entry.name), depth + 1); | ||
| } |
There was a problem hiding this comment.
Directory walk doesn't filter __pycache__ and venv directories
Low Severity
The listDir walk function only skips dot-directories and node_modules when recursing, but doesn't skip __pycache__, venv, .venv, or dist directories. Since this is also used for Python projects (Flask, FastAPI), the pre-computed directory listing sent to the API can be bloated with irrelevant files from virtual environments and bytecode caches, potentially hitting the maxEntries cap before cataloguing meaningful project files.
#354) ## Summary - Add `getOrgBaseUrl()` helper that builds org-scoped subdomain URLs for SaaS (e.g. `https://my-org.sentry.io`) while returning the base URL unchanged for self-hosted instances - Update all 8 URL builder functions in `sentry-urls.ts` to branch on SaaS vs self-hosted: - **SaaS**: subdomain pattern (`https://my-org.sentry.io/issues/...`) - **Self-hosted**: path-based pattern (`https://sentry.company.com/organizations/my-org/issues/...`) - Add `isSaaS()` private helper that checks the current base URL against `isSentrySaasUrl()` - Add self-hosted test coverage verifying all builders produce path-based URLs with no subdomain prepended ## How this affected our `sentry init` command - before: `https://sentry.io/settings/bete-dev/projects/project-created/` - after: `https://bete-dev.sentry.io/settings/projects/project-created/` ## Test plan - [x] `bun run typecheck` passes - [x] `bun run lint` passes - [x] `bun test test/lib/sentry-urls.property.test.ts` passes (45 tests, including 10 new self-hosted tests) - [x] SaaS URLs still use `{org}.sentry.io` subdomain pattern - [x] Self-hosted URLs use `/organizations/{org}/` or `/settings/{org}/` path patterns
## Summary Removes `add-example-trigger` references from the CLI to match the API repo, where this step was already removed. Cleans up the step label, confirm handler logic, `purpose` field on `ConfirmPayload`, and 5 related test cases. ## Changes - Removed `"add-example-trigger"` from `STEP_LABELS` in `clack-utils.ts` - Removed `addExample` / `isExample` logic from `handleConfirm` in `interactive.ts` - Removed optional `purpose` field from `ConfirmPayload` in `types.ts` - Removed 5 test cases covering example-trigger confirm behavior ## Test plan - `bun run lint` passes - `bun test test/lib/init/interactive.test.ts` — remaining confirm tests pass - `git grep "addExample\|add-example-trigger\|add-example" -- src/ test/` returns 0 matches 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…hset paths Previously, applyPatchsetDryRun recorded every patch as applied regardless of its action, while the real applyPatchset silently skipped unknown actions via default: break. Both paths now return an explicit error on unrecognized patch actions so behavior is consistent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The catch block in the suspend/resume loop was showing "Cancelled" for errors like network timeouts and API failures. Use "Error" to match the label used in other error paths in the same file. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # .github/workflows/ci.yml # bun.lock # src/commands/project/create.ts
|
|
||
| spin.start("Scanning project..."); | ||
| const dirListing = precomputeDirListing(directory); | ||
|
|
There was a problem hiding this comment.
Spinner not stopped if precomputeDirListing throws
Low Severity
The spinner is started with spin.start("Scanning project...") but precomputeDirListing(directory) is called outside the try/catch block that follows. If precomputeDirListing throws (e.g., a permissions error from safePath or readdirSync), the spinner is never stopped, potentially leaving the terminal in a broken state.
The merge conflict resolution inadvertently upgraded @mastra/client-js to 1.7.2, pulling in @mastra/core@1.9.0 which introduces a quansync peer dependency that bun 1.3.9 can't resolve with --frozen-lockfile. Restored lockfile from ba6bd73 and re-resolved to keep @mastra/client-js pinned at 1.7.1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…into try/catch Track spinner running state via a mutable SpinState object to guard against calling spin.stop() twice when handleInteractive throws after the spinner was already stopped. Also moved precomputeDirListing inside the existing try/catch as a defensive improvement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Skip all API calls (org resolution, team creation, project creation, DSN fetch) when dryRun is true and return placeholder data instead. Slug validation is kept before the guard so invalid names are still caught in dry-run mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
When applyPatchset fails mid-application, the error response now includes the list of already-applied patches so the server can track partial progress. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| const chosen = abortIfCancelled(selected); | ||
| if (hasRequired && !chosen.includes(REQUIRED_FEATURE)) { | ||
| chosen.unshift(REQUIRED_FEATURE); | ||
| } |
There was a problem hiding this comment.
Redundant includes check always evaluates to true
Low Severity
In handleMultiSelect, the optional array is built by filtering out REQUIRED_FEATURE on line 93, so multiselect can never return a value containing it. The guard !chosen.includes(REQUIRED_FEATURE) on line 122 is therefore always true when hasRequired is true, making the includes check dead logic. The condition simplifies to just if (hasRequired).
Additional Locations (1)
| const parsed = new URL(base); | ||
| parsed.hostname = `${orgSlug}.${parsed.hostname}`; | ||
| return parsed.origin; | ||
| } |
There was a problem hiding this comment.
Exported getOrgBaseUrl only used within defining module
Low Severity
getOrgBaseUrl is exported but appears to only be called by sibling URL-builder functions within the same file (sentry-urls.ts). If no external consumer exists, the export widens the public API surface unnecessarily and could invite misuse (e.g., callers building URLs without the correct SaaS/self-hosted path logic that the higher-level builders provide).
| { pattern: "*", label: "glob expansion (*)" }, | ||
| { pattern: "?", label: "glob expansion (?)" }, | ||
| { pattern: "#", label: "shell comment (#)" }, | ||
| ]; |
There was a problem hiding this comment.
Command validation doesn't block null byte injection
Medium Severity
SHELL_METACHARACTER_PATTERNS does not include the null byte (\0). When commands are passed to spawn with shell: true, a null byte could cause string truncation at the C/shell layer, potentially allowing a crafted command from the remote API to bypass subsequent validation layers. Given commands originate from a remote server, this is worth guarding against.


Summary
Adds
sentry init— an AI-powered wizard that walks users through adding Sentry to their project. It detects the platform, installs the SDK, instruments the code, and configures error monitoring, tracing, and session replay.Changes
Core wizard
initcommand backed by a Mastra AI workflow (hosted at getsentry/cli-init-api) that handles platform detection, SDK installation, and code instrumentationconstants.ts)Security hardening
()subshell bypass,><&redirection/background,$'"\expansion/escaping,{}*?glob/brace expansion,#shell commentVAR=value cmdpattern)cwdvalidation against project directoryreadSyncfailure{ shell: true }instead of hardcodedsh)Performance
list-dirround-trip_prevPhasescross-phase caching to reuse results across workflow steps (perf(init): pre-compute dir listing and send _prevPhases for cross-phase caching #307)Testing & CI
src/lib/banner.tsto break circular importspyOninstead ofmock.modulewhere possible)Eval suite
The eval suite validates that the wizard produces correct, buildable Sentry instrumentation for each supported platform. It uses a 3-phase test architecture:
Phase 1: Wizard run
Each test scaffolds a fresh project from a platform template, then runs the full
sentry initwizard against it. The wizard output (exit code, stdout/stderr, git diff, new files) is captured for the next phases.Phase 2: Hard assertions (deterministic)
Five code-based pass/fail checks that run without any LLM:
package.json/requirements.txt)Sentry.init(orsentry_sdk.init) appears in changed or new files___PUBLIC_DSN___,YOUR_DSN_HERE, etc.)npm run build/ equivalent passes after the wizard's changesPhase 3: LLM judge (per-feature)
For each feature (errors, tracing, replay, logs, profiling, etc.), an LLM judge scores correctness:
feature-docs.json)Platforms
6 platform templates are covered:
express/@sentry/nodenextjs/@sentry/nextjssveltekit/@sentry/sveltekitreact-vite/@sentry/reactpython-flask/sentry-sdkpython-fastapi/sentry-sdkRunning
bun run test:init-eval # all platformsRequires
SENTRY_AUTH_TOKEN,SENTRY_ORG,SENTRY_PROJECT, and optionallyOPENAI_API_KEY(LLM judge is skipped without it).Test Plan
bun run lintandbun run typecheckpass🤖 Generated with Claude Code